// @Author EthanScriptOn
// @Desc
package interceptor

import (
	"errors"
	"fmt"
	"gitee.com/fatzeng/srf_switch_basic_components/tool"
	"gitee.com/fatzeng/srf_switch_basic_components/util"
	"reflect"
	"strings"
)

// InterceptorOptions Interceptor options
// Provides interception capability to the target
type InterceptorOptions struct {
	cacheResult      bool                             // Whether to cache the interception results
	optionBody       func()                           // Intercept functions
	methodCall       *tool.MethodCall                 // The method call object
	conditionChecker ConditionChecker                 // Condition checker
	executed         bool                             // It is used to mark whether the execution has been executed and cannot be repeated
	errorHandler     SrfSwitchInterceptorErrorHandler // Exception Processor
	errorCollection  *tool.ErrorCollection            // Custom exceptions, exception summaries
}

// GenerateInterceptorOptions Generate an interceptor option
func GenerateInterceptorOptions(optionBody interface{}, params ...interface{}) (*InterceptorOptions, error) {
	return structure(optionBody, false, params...)
}

// GenerateWrapperInterceptorOptions Generate a wrapper interceptor option
func GenerateWrapperInterceptorOptions(optionBody interface{}, params ...interface{}) (*InterceptorOptions, error) {
	return structure(optionBody, true, params...)
}

// structure The public logic of the objective function proxy
func structure(optionBody interface{}, wrapUp bool, params ...interface{}) (interceptorOptions *InterceptorOptions, err error) {
	defer tool.HandleErr(&err)
	if err = isValid(optionBody); err != nil {
		return nil, err
	}
	// Create an anonymous function call instance
	instance, err := tool.GenerateFreeMethodCall(reflect.ValueOf(optionBody), params...)
	if err != nil {
		return nil, err
	}
	optionsInstance := InterceptorOptions{
		methodCall:      instance,
		executed:        false,
		errorCollection: new(tool.ErrorCollection),
	}
	// Wrapper function to add the wrapper instance to the first argument bit
	wrappedOptionBody, err := wrapFunctions(wrapUp, &optionsInstance)
	if err != nil {
		return nil, err
	}
	optionsInstance.optionBody = wrappedOptionBody
	return &optionsInstance, nil
}

// isValid Verify the validity of the intercepted function
func isValid(optionBody interface{}) error {
	methodName := util.GetMethodNameWithMethod(optionBody)
	if methodName == "" {
		return errors.New("method name is empty")
	}
	// Our interceptor option only supports how anonymous functions are defined
	if !strings.HasPrefix(methodName, "func") {
		return errors.New("only anonymous functions are supported")
	}
	return nil
}

// wrapFunctions The first parameter of the form parameter of the interceptor option with wrapper is the wrapper itself - *InterceptorOptions
func wrapFunctions(wrapUp bool, optionsInstance *InterceptorOptions) (func(), error) {
	if wrapUp {
		paramsNew, err := util.InsertAtFirst(optionsInstance.methodCall.GetParams(), optionsInstance)
		if err != nil {
			return nil, fmt.Errorf("self packaging failure: [%v]", err)
		}
		optionsInstance.methodCall.SetParams(paramsNew.([]interface{}))
	}
	return func() {
		if !optionsInstance.cacheResult || !optionsInstance.executed {
			optionsInstance.methodCall.Call()
			if err := optionsInstance.methodCall.GetError(); err != nil {
				optionsInstance.errorCollection.WithErrorCollector(err)
			}
		}
	}, nil
}

// IsCacheResult Whether or not to cache blocker results
func (i *InterceptorOptions) IsCacheResult(cacheResult bool) {
	i.cacheResult = cacheResult
}

// SetErrorHandler Set up the interceptor exception handler
func (i *InterceptorOptions) SetErrorHandler(errorHandler SrfSwitchInterceptorErrorHandler) {
	i.errorHandler = errorHandler
}

// SetConditionChecker Condition detection is set to determine whether to enter the interception
func (i *InterceptorOptions) SetConditionChecker(conditionChecker ConditionChecker) {
	i.conditionChecker = conditionChecker
}

// SetParams Set the parameters
func (i *InterceptorOptions) SetParams(params []interface{}) {
	i.methodCall.SetParams(params)
}

// AppendParams Append parameters
func (i *InterceptorOptions) AppendParams(params []interface{}) {
	i.methodCall.AppendParams(params)
}

// OnError Exception handling: Invoke the exception handler to handle the exception
func (i *InterceptorOptions) OnError(err error) {
	if i.errorHandler != nil {
		i.errorHandler.HandleError(err)
	}
}

// IsPassThrough Whether it passes or not, call the condition detection to check whether the interceptor passes or not
func (i *InterceptorOptions) IsPassThrough() (bool, error) {
	if i.conditionChecker != nil {
		return i.conditionChecker.ShouldExecute(i)
	}
	return true, nil
}

// Call Interceptor function calls
func (i *InterceptorOptions) Call() {
	if err := i.isValid(); err != nil {
		i.errorCollection.WithErrorCollector(err)
		return
	}
	i.optionBody()
	i.executed = true
}

// isValid Whether it works
func (i *InterceptorOptions) isValid() (err error) {
	if i.optionBody == nil {
		return errors.New("optionBody is nil")
	}
	if i.methodCall == nil {
		return errors.New("methodCall is nil")
	}
	return
}

// GetResultByIndex Obtain the result of the objective function based on the subscript
func (i *InterceptorOptions) GetResultByIndex(index int) (interface{}, bool) {
	return i.methodCall.GetResultByIndex(index)
}

// GetResultByType Gets the result of the objective function based on the type
func (i *InterceptorOptions) GetResultByType(typ reflect.Type) (interface{}, bool) {
	return i.methodCall.GetResultByType(typ)
}

// GetResultWithoutError Get the result of the objective function without exceptions
func (i *InterceptorOptions) GetResultWithoutError() ([]interface{}, bool) {
	return i.methodCall.GetResultWithoutError()
}

// GetErrorResult Obtain the abnormal execution result of the objective function
func (i *InterceptorOptions) GetResultError() error {
	return i.methodCall.GetResultsError()
}

// GetResult Obtain the execution result of the objective function
func (i *InterceptorOptions) GetResult() ([]interface{}, bool) {
	return i.methodCall.GetResult()
}

// GetError Errors in function runs, such as panic, parameter type mismatch, etc. are caught (excluding exceptions in the result set)
func (i *InterceptorOptions) GetError() error {
	return i.errorCollection.GetError()
}
